<?php

namespace app\models;

use Yii;
use yii\db\Query;
use app\models\db\Role;
use app\models\db\FunctionPoint;
use app\models\db\ManagerRoleRelation;
use app\models\db\RoleFunctionPointRelation;

class FunctionPointExt
{
	const CONTROLLER_DIR = __DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR .'controllers';
	const CONTROLLER_NAMESPACE = 'app\\controllers\\';

	private static $controllerList;

	/**
	 * 将大写驼峰命名转化为小写短横线连接
	 * @param  string $str
	 * @return string
	 * @author millocb
	 * @date   2018-04-24
	 */
	private static function upperCamel2LowerKebab($str)
	{
		$pattern = '/\B[A-Z]\B/'; // 单词[A-Z]单词
		$str = preg_replace_callback($pattern, function ($match) {
			return '-' . strtoupper($match[0]);
		}, $str);
		return strtolower($str);
	}

	/**
	 * 将小写短横线转化成大写驼峰
	 * @param  string $str
	 * @return string
	 * @author milloc
	 * @date   2018-04-24
	 */
	private static function lowerKebab2UpperCamel($str)
	{
		$pattern = '/\b-[a-z]\B/'; // 单词-[a-z]单词
		$str = preg_replace_callback($pattern, function ($match) {
			return strtoupper($match[0][1]);
		}, $str);
		return strtoupper($str[0]) . substr($str, 1);
	}

	/**
	 * 获取全部的控制器
	 * 通过遍历文件的方法
	 * @return array 返回小写短横线命名的控制器
	 * @author milloc
	 * @date   2018-04-23
	 */
	public static function getControllerList()
	{
		if (!empty(self::$controllerList)) {
			return self::$controllerList;
		}
		// 通过遍历文件来获取全部控制器
		// 改方法很可能产生问题
		$dir =  scandir(self::CONTROLLER_DIR); // 不是目录会产生warning
		$dir = $dir ? $dir : [];
		$controllerList = [];
		foreach ($dir as $file) {
			if ($file == '.' || $file == '..') {
				continue;
			}
			if (preg_match('/Controller\.php$/', $file)) {
				$controller = substr($file, 0, -14);
				$controllerList[] = self::upperCamel2LowerKebab($controller);	
			}
		}

		return self::$controllerList = $controllerList;
	}

	/**
	 * 获取全部的动作
	 * 通过get_class_methods的方法
	 * @return array
	 * @author milloc
	 * @date   2018-04-23
	 */
	public static function getAllActionList()
	{
		$cotrollerList = self::getControllerList();
		$actionList = [];
		foreach ($controllerList as $controllerId) {
			$actionList[$controllerId][] = self::getActionList($controllerId);
		}
		return $actionList;
	}

	/**
	 * 通过控制器获取actionList
	 * 触发自动加载类
	 * @param  string $controller
	 * @return array 返回小写短横线命名的动作
	 * @author milloc
	 * @date   2018-04-24
	 */
	public static function getActionList($controller = '')
	{
		$actionList = [];
		if (in_array($controller, self::getControllerList())) {
			$className = self::CONTROLLER_NAMESPACE . self::lowerKebab2UpperCamel($controller) . 'Controller';
			if (class_exists($className)) {
				$methodList = get_class_methods($className);
				foreach ($methodList as $method) {
					if (preg_match('/^action[A-Z]/', $method)) {
						$actionList[] = self::upperCamel2LowerKebab(substr($method, 6));
					}
				}
			}
		}
		return $actionList;
	}

	/**
	 * 获取功能点树
	 * @return array
	 * @author milloc
	 * @date   2018-04-24
	 */
	public static function getFunctionPointTree()
	{
		$list = FunctionPoint::find()->select(['id', 'name', 'description', 'controller', 'action', 'parent_id'])->where(['is_deleted' => FunctionPoint::IS_DELETED_NO])->asArray()->all();

		$funPointTree = [];
		if (!empty($list)) {
			$stack = []; // splstack 不能保存引用
			$stack_length = 0;
			foreach ($list as $key => $value) {
				if ($value['parent_id'] === FunctionPoint::PARENT_ID_ROOT) {
					$funPointTree[] = $value;
					$stack[$stack_length++] = &$funPointTree[$stack_length - 1];
					unset($list[$key]);
				}
			}

			while ($stack_length) {
				$top = &$stack[--$stack_length];
				unset($stack[$stack_length]);
				foreach ($list as $key => $value) {
					if ($value['parent_id'] == $top['id']) {
						$top['child'][] = $value;
						$stack[$stack_length++] = &$top['child'][count($top['child']) - 1];
						unset($list[$key]);
					}
				}
			}
		}

		// todo
		// 添加排序
		return $funPointTree;
	}

	/**
	 * 获取功能点列表
	 * @return array
	 * @author milloc
	 * @date   2018-04-24
	 */
	public static function getFunctionPointList()
	{
		$list = FunctionPoint::find()->select(['id', 'name', 'description', 'controller', 'action', 'parent_id'])->where(['is_deleted' => FunctionPoint::IS_DELETED_NO])->asArray()->all();
		$funPointList = [];
		if (!empty($list)) {
			$stack = new \SplStack;

			foreach ($list as $key => $value) {
				if ($value['parent_id'] == FunctionPoint::PARENT_ID_ROOT) {
					$stack->push(['level' => 1, 'item' => $value]);
					unset($list[$key]);
				}
			}

			while (!$stack->isEmpty()) {
				$top = $stack->pop();
				$level = $top['level'];
				$item = $top['item'];
				$item['level'] = $level++;
				$funPointList[] = $item;
				foreach ($list as $key => $value) {
					if ($value['parent_id'] == $item['id']) {
						$stack->push(['level' => $level, 'item' => $value]);
						unset($list[$key]);
					}
				}
			}
		}

		// todo
		// 添加排序
		return $funPointList;
	}

	/**
	 * 获取树合法的叶子节点
	 * 合法的叶子节点 controller && action
	 * 用于用户组添加功能点
	 * @return array '一级节点的名字>...>叶子节点的父级节点' => '叶子节点'
	 * @author milloc
	 * @date   2018-04-26
	 */
	public static function getFunctionPointTreeLeaf()
	{
		$list = self::getFunctionPointTree();

		$stack = new \SplStack; // parent:name, item:node

		foreach ($list as $item) {
			$stack->push(['parent' => '', 'item' => $item]);
		 }

		 $funPointTreeLeaf = [];
		 while (!$stack->isEmpty()) {
			$top = $stack->pop();
			$parent = $top['parent'];
			$item = $top['item'];
			if (empty($item['child'])) {
				if (!empty($item['controller']) && !empty($item['action'])) {
					$funPointTreeLeaf[$parent][] = $item;
				}
			} else {
				$parent = $parent ? $parent . '>' . $item['name'] : $item['name'];
				foreach ($item['child'] as $child) {
					$stack->push(['parent' => $parent, 'item' => $child]);
				}
			}
		 }

		 // todo
		 // 添加排序
		 return $funPointTreeLeaf;
	}

	/**
	 * 获取manager的权限列表
	 * 返回的权限列表是同时含有controller和action的
	 * @param  int $manager_id
	 * @return array [['controller', 'action']]
	 * @author milloc
	 * @date   2018-04-27
	 */
	public static function getFunctionPointListByManagerId($manager_id)
	{
		// 查找关联的已经启用的role_id
		$role_id = (new Query)->from(['re' => ManagerRoleRelation::tableName()])->leftJoin(['r' => Role::tableName()], 're.role_id = r.id')->where(['re.manager_id' => $manager_id, 'r.is_enabled' => Role::IS_ENABLED_YES, 'r.is_deleted' => Role::IS_DELETED_NO])->select(['r.id'])->column();

		$funciont_point_list = [];
		if ($role_id) {
			$funciont_point_list = (new Query)->from(['r' => RoleFunctionPointRelation::tableName()])->leftJoin(['f' => FunctionPoint::tableName()], 'r.function_point_id = f.id')->where(['r.role_id' => $role_id, 'f.is_deleted' => FunctionPoint::IS_DELETED_NO])->andWhere(['and', 'not isnull(f.controller) and f.controller <> ""', 'not isnull(f.action) and f.action <> ""'])->select(['f.controller', 'f.action'])->distinct()->all();
		}

		return $funciont_point_list;
	}
}